package com.reflection;

import com.reflection.homework.Student;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class ReflectionDemo {

    /**
     * 1.什么是反射
     *   在代码运行的时候可以动态的获取类中的一些信息，可以改变类中的一些内容
     * 2.Class类
     *   表示正在运行的java应用程序中的类和接口
     *   把需要反射的类作为 Class的类对象，才可以使用反射获取当前类的信息
     *   如果需要使用反射，必须把某个类或类实例化对象 转成 Class对象
     * 3.使用反射API
     *   3.1 获取Class的对象
     *     3.1 如果有实例化对象  Person p = new Person
     *         Class clazz = p.getClass();
     *     3.2 如果有类  Person
     *         这个类必须得导包才能使用
     *         Class clazz = Person.class;
     *     3.3 如果有类的字符串形式的路径
     *         Class clazz = Class.forName("com.mysql.jdbc.Driver");
     */

    /**
     * 1.通过反射获取类自己本身的一些属性
     */
    public void aboutClassProperty(Class clazz){

        //获取包名
        String packageName = clazz.getPackage().getName();
        System.out.println(packageName);

        //获取类名
        System.out.println(clazz.getSimpleName());

        //获取完整类名
        System.out.println(clazz.getName());

    }

    /**
     * 2.关于类中的属性的一些方法
     */
    public void aboutClassVariable(Class clazz) throws NoSuchFieldException {

        //2.1获取当前类及其父类中的所有 public 的属性
        Field[] fields = clazz.getFields();
        System.out.println(Arrays.toString(fields));

        //2.2获取当前类或父类中的指定的 public 的属性
        //作用：按照指定的字段进行获取，可以改变原来的内容或权限
        Field height = clazz.getField("height");
        System.out.println(height);

        //2.3获取当前类中的所有属性，包括 public 和 private
        Field[] declaredFields = clazz.getDeclaredFields();
        System.out.println(Arrays.toString(declaredFields));

        //2.4获取当前类中的所有属性中的指定一个属性，不能获取父类中的属性
        Field gender = clazz.getDeclaredField("gender");
        System.out.println(gender);


    }

    /**
     * 3.关于构造方法的一些常用方法
     * @param clazz
     */
    public void aboutConstructor(Class clazz) throws NoSuchMethodException {

        //3.1 获取当前类中的所有 public 的构造方法
        Constructor[] constructors = clazz.getConstructors();
        System.out.println(Arrays.toString(constructors));

        //3.2 获取当前类中的所有的 public 的构造方法中的指定方法
        Constructor constructor = clazz.getConstructor(int.class,String.class);
        System.out.println(constructor);

        //3.3 获取当前类中的所有的构造方法，包括 private
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        System.out.println(Arrays.toString(declaredConstructors));

        //3.4 获取当前类中的所有的构造方法中的指定方法，包括 private
        Constructor declaredConstructor = clazz.getDeclaredConstructor(int.class, String.class, String.class);
        System.out.println(declaredConstructor);

    }

    /**
     * 4.关于普通方法的一些常用方法
     * @param clazz
     */
    public void aboutMethod(Class clazz) throws NoSuchMethodException {

        //4.1 获取当前类和所有父类中的 public 的方法
        Method[] methods = clazz.getMethods();
        System.out.println(Arrays.toString(methods));

        //4.2 获取当前类或父类中的指定 public 的方法
        Method walk = clazz.getMethod("walk");
        System.out.println(walk);

        //4.3 获取当前类中的所有普通方法，不包括父类
        Method[] declaredMethods = clazz.getDeclaredMethods();
        System.out.println(Arrays.toString(declaredMethods));

        //4.4 获取当前类中指定的普通方法，可以是private 不包括父类
        Method jump = clazz.getDeclaredMethod("jump", int.class);
        System.out.println(jump);

    }

    /**
     * 5.关于其他内容的一些常用方法
     * @param clazz
     */
    public void aboutOther(Class clazz){

        //5.1 获取当前 Class 对象的父类的 Class
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);

        //5.2 获取当前 Class 的所有 public 的内部类，可以包含静态内部类
        Class[] innerClasses = clazz.getClasses();
        System.out.println(Arrays.toString(innerClasses));

        //5.3 判断父类与子类的关系
        if (clazz.getSuperclass() == Biology.class) {
            System.out.println("当前类是Biology的子类");
        }

        //5.4 如果想判断当前类是否为某个类的子类或多重子类
        //    注意：把等级高的类放在左侧做调用，把等级低的类当做参数进行判断
        if (Biology.class.isAssignableFrom(clazz)) {
            System.out.println("当前类有Biology派生出来的");
        }
    }

    /**
     * 6.通过反射调用成员变量
     * @param clazz
     */
    public void useProperty(Class clazz) throws Exception {

//        Person p = (Person)clazz.newInstance();
//
//        Field gender = clazz.getDeclaredField("gender");
//        System.out.println(gender);
//
//        //把当前的私有属性设置为允许访问
//        gender.setAccessible(true);
//
//        Object obj = clazz.newInstance();
//
//        gender.set(obj,"boy");
//
//        Object genderNew = gender.get(obj);
//
//        System.out.println(genderNew);

//        Object objClazz = clazz.newInstance();
//
//        Field[] declaredFields = clazz.getDeclaredFields();
//        for (Field declaredField : declaredFields) {
//            declaredField.setAccessible(true);
//            declaredField.set(objClazz,"");
//        }

    }

    /**
     * 7.通过反射调用成员方法
     * @param clazz
     */
    public void useMethod(Class clazz) throws Exception {

        //获取实例
        Object objClazz = clazz.newInstance();

        Method method_jump = clazz.getDeclaredMethod("jump",int.class);

        method_jump.setAccessible(true);

        System.out.println(method_jump.invoke(objClazz, 100));


    }
    public static void main(String[] args) throws Exception {

        Person person = new Person();
        //第一种 使用对象名创建 Class 类的对象
//        Class cla_Person = person.getClass();
        //第二种 使用类名创建 Class 类的对象
//        Class cla_Person = Person.class;
        //第三种 使用类的路径创建 Class 类的对象
        Class cla_Person = Class.forName("com.reflection.Person");


        ReflectionDemo reflectionDemo = new ReflectionDemo();

        reflectionDemo.aboutClassProperty(cla_Person);
        System.out.println("===================================================");

        reflectionDemo.aboutClassVariable(cla_Person);
        System.out.println("===================================================");

        reflectionDemo.aboutConstructor(cla_Person);
        System.out.println("===================================================");

        reflectionDemo.aboutMethod(cla_Person);
        System.out.println("===================================================");


        reflectionDemo.aboutOther(Student.class);
        System.out.println("===================================================");

        reflectionDemo.useProperty(cla_Person);
        System.out.println("===================================================");

        reflectionDemo.useMethod(cla_Person);
    }

}
